package modeller.databasedesignmodel.servicedemand.transaction.sqlquery.demandFetcher;

import modeller.databasedesignmodel.DatabaseDesign;
import modeller.databasedesignmodel.query.*;
import modeller.databasedesignmodel.relation.Relation;
import modeller.databasedesignmodel.servicedemand.IServiceDemand;
import modeller.databasedesignmodel.servicedemand.transaction.sqlquery.SQLQueryServiceDemand;
import modeller.databasedesignmodel.transaction.SQLQuery;
import queped.NeptuneHelperFunctions;

/**
 * Created by:  Jason Ye
 * Date:        23/03/2012
 * Time:        09:20
 */
public class SortedCalculatorVisitor extends AbstractSQLQueryCalculatorVisitor {

    public SortedCalculatorVisitor(DatabaseDesign design) {
        super(design);
    }

    @Override
    public IServiceDemand calculate(SequentialScanQuery query) {

        // BD
        Relation relation = query.getRelation().first;
        return new SQLQueryServiceDemand(
                relation.getBNumDbPages() * design.getDAverageIOTime(),
                query.getRelation().getFirst());

    }

    @Override
    public IServiceDemand calculate(EqualitySearchQuery query) {

        // D * log2(B)

        //assume one relation per query, and required relation is at front of list
        Relation relation = query.getRelation().first;
        double D = design.getDAverageIOTime();

        return new SQLQueryServiceDemand(
                D * getLogBase2ofB(relation),
                relation
        );
    }

    @Override
    public IServiceDemand calculate(EqualityWithRangeSearchQuery query) {

        // D(log2(B)+#matching pages )
        Relation relation = query.getRelation().first;
        double D = design.getDAverageIOTime();
        //assume one relation per query, and required relation is at front of list
        double logBase2ofB = getLogBase2ofB(relation);

        int numMatchingRows = query.getNumMatchingRows();
        double recordsPerPage = Math.ceil((double) design.getDBPageSize() / (double) relation.getExpectedRowSize());
        double numMatchingPages = Math.ceil(((double) numMatchingRows) / recordsPerPage);

        return new SQLQueryServiceDemand(
                D * (logBase2ofB + numMatchingPages), relation
        );
    }

    @Override
    public IServiceDemand calculate(InsertQuery query) {
        // Search + BD = D * log2(B) + BD
        return getSearchPlusBD(query);
    }

    @Override
    public IServiceDemand calculate(UpdateDeleteQuery query) {

        // Search + BD = D * log2(B) + BD
        return getSearchPlusBD(query);
    }

    private double getLogBase2ofB(Relation relation) {
        return NeptuneHelperFunctions.logBase2(relation.getBNumDbPages());
    }

    private SQLQueryServiceDemand getSearchPlusBD(SQLQuery query) {
        return new SQLQueryServiceDemand(
                (
                        design. getDAverageIOTime()
                                * getLogBase2ofB(query.getRelation().first)
                )
                        + getBDForQuery(query),
                query.getRelation().getFirst()
        );
    }
}